package org.dromara.bean.util.wxApi;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import org.dromara.bean.config.WxPaymentConfig;
import org.dromara.bean.constant.OrderTypeConstant;
import org.dromara.bean.domain.HisGoods;
import org.dromara.bean.domain.HisGoodsOrder;
import org.dromara.bean.domain.HisMemberRefund;
import org.dromara.bean.domain.HisRegistration;
import org.dromara.bean.domain.pay.WxNativePayRequest;
import org.dromara.bean.mapper.BaseMemberRefundMapper;
import org.dromara.bean.mapper.HisGoodsOrderMapper;
import org.dromara.bean.mapper.HisRegistrationMapper;
import org.dromara.bean.service.IBaseMemberRefundService;
import org.dromara.bean.service.IBasePaymentLogService;
import org.dromara.bean.service.IHisRegistrationService;
import org.dromara.common.core.domain.model.WxH5PayRequest;
import org.dromara.common.core.domain.model.WxRefundResponseDTO;
import org.dromara.common.core.service.DictService;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StopWatch;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.*;

/**
 * 微信退款处理类
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class WXHandleUtil {

    private static final String TRANS_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
    // 请求器的配置
    private static RequestConfig requestConfig;
    private WxPaymentConfig wxPayConfig;
    private CloseableHttpClient httpClient;
    private Verifier verifier;

    private final DictService iBaseDictService;

    private final HisRegistrationMapper registrationMapper;

    private final HisGoodsOrderMapper goodsOrderMapper;

    private final BaseMemberRefundMapper baseMemberRefundService;
    /**
     * 初始化httpclient
     */
    private void init() {
        wxPayConfig = iBaseDictService.getDictToObj("wx_pay_config", new WxPaymentConfig());
        StringBuilder privateKey = new StringBuilder();
        String mchSerialNo = wxPayConfig.getMchSerialNo();
        String apiV3Key = wxPayConfig.getApiV3Key();
        try {
            FileInputStream instream = new FileInputStream(wxPayConfig.getCertKey());
//            FileInputStream instream = new FileInputStream("C:\\Users\\java\\Downloads\\apiclient_key.pem");
            int len = 0;
            byte[] buf = new byte[10240];
            while ((len = instream.read(buf)) != -1) {
                privateKey.append(new String(buf, 0, len));
            }
            // 获取证书管理器实例
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            // 加载商户私钥（privateKey：私钥字符串）
            PrivateKey merchantPrivateKey = PemUtil
                .loadPrivateKey(new ByteArrayInputStream(privateKey.toString().getBytes("utf-8")));
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(wxPayConfig.getMchID(), new WechatPay2Credentials(wxPayConfig.getMchID(),
                new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
            // ... 若有多个商户号，可继续调用putMerchant添加商户信息

            // 从证书管理器中获取verifier
            verifier = certificatesManager.getVerifier(wxPayConfig.getMchID());
            WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(wxPayConfig.getMchID(), mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));

            // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
            httpClient = builder.build();
            // 根据默认超时限制初始化requestConfig
            requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(30000).build();
        } catch (Exception e) {
            log.error("微信请求client初始化失败！", e);
        }
    }

    /**
     * native支付
     */
    public String nativePay(WxNativePayRequest request) {
        init();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String response = null;
        String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/native";
        try {
            String data = JSONObject.toJSONString(request);
            HttpPost httpPost = new HttpPost(url);//退款接口
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            httpPost.setEntity(new StringEntity(data));
            CloseableHttpResponse closeableHttpResponse = httpClient.execute(httpPost);
            response = EntityUtils.toString(closeableHttpResponse.getEntity());
            log.warn("微信回文：【{}】", response);
        } catch (Exception e) {
            log.error("wx nativePay error", e);
        }
        stopWatch.stop();
        log.warn("请求微信用时：【{}s】", stopWatch.getTotalTimeSeconds());
        JSONObject jsonObject = JSONObject.parseObject(response);
        if (ObjectUtil.isNotEmpty(jsonObject.getString("status")) &&
            ("SUCCESS".equals(jsonObject.getString("status")) ||
                "PROCESSING".equals(jsonObject.getString("status")))) {
            return JSONObject.parseObject(response).getString("code_url");
        } else {
            return JSONObject.parseObject(response).getString("code_url");
        }
    }

    /**
     * H5支付
     */
    public String h5Pay(WxH5PayRequest request) {
        init();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String response = null;
        String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/h5";
        try {
            String data = JSONObject.toJSONString(request);
            HttpPost httpPost = new HttpPost(url);//退款接口
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            httpPost.setEntity(new StringEntity(data));
            CloseableHttpResponse closeableHttpResponse = httpClient.execute(httpPost);
            response = EntityUtils.toString(closeableHttpResponse.getEntity());
            log.warn("微信回文：【{}】", response);
        } catch (Exception e) {
            log.error("wx h5Pay error", e);
        }
        stopWatch.stop();
        log.warn("请求微信用时：【{}s】", stopWatch.getTotalTimeSeconds());
        JSONObject jsonObject = JSONObject.parseObject(response);
        if (ObjectUtil.isNotEmpty(jsonObject.getString("status")) &&
            ("SUCCESS".equals(jsonObject.getString("status")) ||
                "PROCESSING".equals(jsonObject.getString("status")))) {
            return JSONObject.parseObject(response).getString("h5_url");
        } else {
            return JSONObject.parseObject(response).getString("h5_url");
        }
    }

    /**
     * 退款
     *
     * @param refundId  退款ID
     * @param refundAmt 退款金额
     * @param totalAmt  总金额
     */
    public WxRefundResponseDTO refund(String refundId, Integer refundAmt, Integer totalAmt) {
        init();
        HisMemberRefund memberRefund = baseMemberRefundService.selectById(refundId);
        if (ObjectUtil.isEmpty(memberRefund)) {
            return new WxRefundResponseDTO(false, "会员退款信息不存在");
        }
        String orderNumber;
        switch (memberRefund.getOrderType()) {
            case OrderTypeConstant.ORDER_TYPE_BOOK_EXAMINE:
                LambdaQueryWrapper<HisRegistration> lqw = new LambdaQueryWrapper<>();
                lqw.eq(HisRegistration::getOrderNumber, memberRefund.getOrderTreatId());
                lqw.in(HisRegistration::getStatus,500);
                lqw.eq(HisRegistration::getDelFlag, 0);
                HisRegistration hisRegistration = registrationMapper.selectOne(lqw);

                if (ObjectUtil.isEmpty(hisRegistration)) {
                    return new WxRefundResponseDTO(false, "订单不存在或状态不为待退款！");
                }
                orderNumber = hisRegistration.getOrderNumber();
                break;
            default:
                log.warn("不支持的单据类型 memberRefundId = {}", memberRefund.getId());
                return new WxRefundResponseDTO(false, "不支持的单据类型 memberRefundId = {}" + memberRefund.getId());
        }

        JSONObject baseInfo = new JSONObject();
        baseInfo.put("out_trade_no", orderNumber);//微信支付订单号
        baseInfo.put("out_refund_no", refundId);//商户退款单号
        baseInfo.put("notify_url", wxPayConfig.getRefundNotifyUrl());//回调URL
//        baseInfo.put("reason", memberRefund.getRefundCause());//退款原因   --？？乱码解决不了，放弃了

        JSONObject amount = new JSONObject();
        amount.put("refund", refundAmt);//退款金额，单位为分，只能为整数，不能超过原订单支付金额。
        amount.put("total", totalAmt);//原支付交易的订单总金额，单位为分，只能为整数。
        amount.put("currency", "CNY");//微信支付订单号
        baseInfo.put("amount", amount);
        log.warn("微信退款请求参数：【{}】", baseInfo.toString());
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String response;
        try {
            response = doRefund(wxPayConfig.getRefundUrl(), baseInfo.toJSONString());
            log.warn("微信退款回文：【{}】", response);
        } catch (Exception e) {
            log.error("wx refund error", e);
            return new WxRefundResponseDTO(false, "微信退款请求失败");
        }
        stopWatch.stop();
        log.warn("请求微信退款用时：【{}s】", stopWatch.getTotalTimeSeconds());
        JSONObject jsonObject = JSONObject.parseObject(response);
        if (ObjectUtil.isNotEmpty(jsonObject.getString("status")) &&
            ("SUCCESS".equals(jsonObject.getString("status")) ||
                "PROCESSING".equals(jsonObject.getString("status")))) {
            memberRefund.setRealRefundTime(DateUtil.date());
            baseMemberRefundService.updateById(memberRefund);
            return new WxRefundResponseDTO(true, "成功");
        } else {
            return new WxRefundResponseDTO(false, jsonObject.getString("message"));
        }
    }






    /**
     * 申请退款
     */
    public String doRefund(String url, String data) throws Exception {
        init();
        log.warn("微信退款，请求URL：【{}】，请求包：【{}】", url, data);
        HttpPost httpPost = new HttpPost(url);//退款接口
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json;charset=GBK");
        httpPost.setEntity(new StringEntity(data));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        return EntityUtils.toString(response.getEntity());
    }

    public WxPayResponseDTO doPay(String openId, Integer amount, String ip, String desc) {
        Map map = payForUser(IdWorker.getIdStr(), openId, amount.longValue(), ip, desc);
        if (ObjectUtil.isEmpty(map)) {
            return new WxPayResponseDTO(false);
        }
        log.warn(JSONObject.toJSONString(map));
        return new WxPayResponseDTO(true);
    }


    /**
     * 微信提现（企业付款）
     */
    public Map payForUser(String orderNo, String openId, Long amount, String ip, String desc) {
        init();
        Map map;
        SortedMap<String, Object> params = new TreeMap<>();
//        params.put("mch_appid", wxPayConfig.getAppId());
        params.put("mch_appid", "wx8e8a3693c540d691");
        params.put("mchid", wxPayConfig.getMchID());
        params.put("partner_trade_no", orderNo);
        params.put("nonce_str", IdWorker.get32UUID());
        params.put("openid", openId);
        params.put("amount", amount.toString());
        params.put("spbill_create_ip", ip);
        params.put("desc", desc);
        params.put("check_name", "NO_CHECK");
        String sign = createSign(params);
        params.put("sign", sign);
        String xmlInfo = getRequestXml(params);
        log.debug("WxService==payForUser==xmlInfo==" + xmlInfo);
        try {
            CloseableHttpResponse response = WxPost(TRANS_URL, xmlInfo, true);
            String transfersXml = EntityUtils.toString(response.getEntity(), "utf-8");
            log.debug("transfersXml---" + transfersXml);
            map = doXMLParse(transfersXml);
        } catch (Exception e) {
            log.error("wx payForUser error", e);
            return null;
        }
        return map;
    }

    /**
     * 生成签名
     *
     * @return
     */
    private String createSign(SortedMap<String, Object> parameters) {
        StringBuilder sb = new StringBuilder();
        Set es = parameters.entrySet();
        for (Object e : es) {
            Map.Entry entry = (Map.Entry) e;
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v)
                && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k).append("=").append(v).append("&");
            }
        }
        sb.append("key=").append(wxPayConfig.getPayKey());
        log.debug("签名结果==" + sb.toString());
        return MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
    }

    /**
     * 请求xml组装
     *
     * @return
     */
    private static String getRequestXml(SortedMap<String, Object> parameters) {
        StringBuilder sb = new StringBuilder();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        for (Object e : es) {
            Map.Entry entry = (Map.Entry) e;
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {
                sb.append("<").append(key).append(">").append("<![CDATA[").append(value).append("]]></").append(key).append(">");
            } else {
                sb.append("<").append(key).append(">").append(value).append("</").append(key).append(">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * xml解析
     *
     * @return
     */
    private static Map doXMLParse(String strxml) throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
        if ("".equals(strxml)) {
            return null;
        }
        Map m = new HashMap();
        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        for (Object aList : list) {
            Element e = (Element) aList;
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }
            m.put(k, v);
        }
        //关闭流
        in.close();
        return m;
    }

    private static String getChildrenText(List children) {
        StringBuilder sb = new StringBuilder();
        if (!children.isEmpty()) {
            for (Object aChildren : children) {
                Element e = (Element) aChildren;
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<").append(name).append(">");
                if (!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</").append(name).append(">");
            }
        }
        return sb.toString();
    }

    /**
     * 发送post请求
     *
     * @param url          请求地址
     * @param outputEntity 发送内容
     * @param isLoadCert   是否加载证书
     */
    public CloseableHttpResponse WxPost(String url, String outputEntity, boolean isLoadCert) throws Exception {
        log.warn("发起微信请求，URL：【{}】，请求实体：【{}】，是否加载证书：【{}】", url, outputEntity, isLoadCert);
        HttpPost httpPost = new HttpPost(url);
        // 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(new StringEntity(outputEntity, "UTF-8"));
        if (isLoadCert) {
            // 加载含有证书的http请求
            return HttpClients.custom().setSSLSocketFactory(initCert()).build().execute(httpPost);
        } else {
            return HttpClients.custom().build().execute(httpPost);
        }
    }

    /**
     * 加载证书
     */
    public SSLConnectionSocketFactory initCert() throws Exception {
        FileInputStream instream = null;
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        File file = ResourceUtils.getFile(wxPayConfig.getSslCert());
//        File file = ResourceUtils.getFile("F:\\桌面\\互联网医院\\smart-hospital-server\\internet-hospital\\internet-hospital-module-system\\src\\main\\resources\\cert\\apiclient_cert.p12");
        instream = new FileInputStream(file);
        keyStore.load(instream, wxPayConfig.getMchID().toCharArray());

        if (null != instream) {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, wxPayConfig.getMchID().toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, new DefaultHostnameVerifier());
        return sslsf;
    }

    static final int KEY_LENGTH_BYTE = 32;
    static final int TAG_LENGTH_BIT = 128;

    public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
        throws GeneralSecurityException, IOException {
        init();
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);

            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            log.error("wx decryptToString error", e);
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            log.error("wx decryptToString error", e);
            throw new IllegalArgumentException(e);
        }
    }

    /**
     *  退款
     *
     * @param refundId 退款ID
     */
    public WxRefundResponseDTO refundResponseDTO(String refundId) {
        init();
        HisMemberRefund memberRefund = baseMemberRefundService.selectById(refundId);
        if (ObjectUtil.isEmpty(memberRefund)) {
            return new WxRefundResponseDTO(false, "会员退款信息不存在");
        }
        String orderNumber;
        switch (memberRefund.getOrderType()) {
            case OrderTypeConstant.ORDER_REGISTRATION_TREAT:
                HisRegistration registration = registrationMapper.selectById(memberRefund.getOrderTreatId());
                if (ObjectUtil.isEmpty(registration)) {
                    return new WxRefundResponseDTO(false, "订单不存在或状态不为待退款！");
                }
                orderNumber = registration.getOrderNumber();
                break;
            case OrderTypeConstant.ORDER_COMMODITY_TREAT:
                HisGoodsOrder goodsOrder = goodsOrderMapper.selectById(memberRefund.getOrderTreatId());
                if (ObjectUtil.isEmpty(goodsOrder)) {
                    return new WxRefundResponseDTO(false, "订单不存在或状态不为待退款！");
                }
                orderNumber = goodsOrder.getOrderNumber();
                break;
            default:
                log.warn("不支持的单据类型 memberRefundId = {}", memberRefund.getId());
                return new WxRefundResponseDTO(false, "不支持的单据类型 memberRefundId = {}" + memberRefund.getId());
        }

        JSONObject baseInfo = new JSONObject();
        baseInfo.put("out_trade_no", orderNumber);//微信支付订单号
        baseInfo.put("out_refund_no", refundId);//商户退款单号
        baseInfo.put("notify_url", wxPayConfig.getRefundNotifyUrl());//回调URL
//        baseInfo.put("reason", memberRefund.getRefundCause());//退款原因   --？？乱码解决不了，放弃了

        JSONObject amount = new JSONObject();
        Double money = memberRefund.getMoney();
        BigDecimal moneyBigDecimal = BigDecimal.valueOf(money);
        long refundAmountInCents = moneyBigDecimal.multiply(new BigDecimal(100)).longValue();

        amount.put("refund", refundAmountInCents);//退款金额，单位为分，只能为整数，不能超过原订单支付金额。
        amount.put("total", refundAmountInCents);//原支付交易的订单总金额，单位为分，只能为整数。
        amount.put("currency", "CNY");//微信支付订单号
        baseInfo.put("amount", amount);
        log.warn("微信退款请求参数：【{}】", baseInfo.toString());
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String response;
        try {
            response = doRefund(wxPayConfig.getRefundUrl(), baseInfo.toJSONString());
            log.warn("微信退款回文：【{}】", response);
        } catch (Exception e) {
            log.error("wx refund error", e);
            return new WxRefundResponseDTO(false, "微信退款请求失败");
        }
        stopWatch.stop();
        log.warn("请求微信退款用时：【{}s】", stopWatch.getTotalTimeSeconds());
        JSONObject jsonObject = JSONObject.parseObject(response);
        if (ObjectUtil.isNotEmpty(jsonObject.getString("status")) &&
            ("SUCCESS".equals(jsonObject.getString("status")) ||
                "PROCESSING".equals(jsonObject.getString("status")))) {
            memberRefund.setRealRefundTime(DateUtil.date());
            baseMemberRefundService.updateById(memberRefund);
            return new WxRefundResponseDTO(true, "成功");
        } else {
            return new WxRefundResponseDTO(false, jsonObject.getString("message"));
        }
    }


    /**
     *  商城 挂号退款
     *
     * @param refundId 退款ID
     */
    public WxRefundResponseDTO refundCommidityExamine(String refundId) {
        init();
        HisMemberRefund memberRefund = baseMemberRefundService.selectById(refundId);
        if (ObjectUtil.isEmpty(memberRefund)) {
            return new WxRefundResponseDTO(false, "会员退款信息不存在");
        }
        String orderNumber;
        switch (memberRefund.getOrderType()) {
            case OrderTypeConstant.ORDER_COMMODITY_TREAT:
                HisGoodsOrder goodsOrder =  goodsOrderMapper.selectById(memberRefund.getOrderTreatId());

                if (ObjectUtil.isEmpty(goodsOrder)) {
                    return new WxRefundResponseDTO(false, "订单不存在或状态不为待退款！");
                }
                orderNumber = goodsOrder.getOrderNumber();
                break;
            case OrderTypeConstant.ORDER_REGISTRATION_TREAT:
                HisRegistration service = registrationMapper.selectById(memberRefund.getOrderTreatId());
                if (ObjectUtil.isEmpty(service)) {
                    return new WxRefundResponseDTO(false, "订单不存在或状态不为待退款！");
                }
                orderNumber = service.getOrderNumber();
                break;
            default:
                log.warn("不支持的单据类型 memberRefundId = {}", memberRefund.getId());
                return new WxRefundResponseDTO(false, "不支持的单据类型 memberRefundId = {}" + memberRefund.getId());
        }

        JSONObject baseInfo = new JSONObject();
        baseInfo.put("out_trade_no", orderNumber);//微信支付订单号
        baseInfo.put("out_refund_no", refundId);//商户退款单号
        baseInfo.put("notify_url", wxPayConfig.getRefundNotifyUrl());//回调URL
//        baseInfo.put("reason", memberRefund.getRefundCause());//退款原因   --？？乱码解决不了，放弃了

        JSONObject amount = new JSONObject();
        Double money = memberRefund.getMoney();
        BigDecimal moneyBigDecimal = BigDecimal.valueOf(money);
        long refundAmountInCents = moneyBigDecimal.multiply(new BigDecimal(100)).longValue();

        amount.put("refund", refundAmountInCents);//退款金额，单位为分，只能为整数，不能超过原订单支付金额。
        amount.put("total", refundAmountInCents);//原支付交易的订单总金额，单位为分，只能为整数。
        amount.put("currency", "CNY");//微信支付订单号
        baseInfo.put("amount", amount);
        log.warn("微信退款请求参数：【{}】", baseInfo.toString());
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String response;
        try {
            response = doRefund(wxPayConfig.getRefundUrl(), baseInfo.toJSONString());
            log.warn("微信退款回文：【{}】", response);
        } catch (Exception e) {
            log.error("wx refund error", e);
            return new WxRefundResponseDTO(false, "微信退款请求失败");
        }
        stopWatch.stop();
        log.warn("请求微信退款用时：【{}s】", stopWatch.getTotalTimeSeconds());
        JSONObject jsonObject = JSONObject.parseObject(response);
        if (ObjectUtil.isNotEmpty(jsonObject.getString("status")) &&
            ("SUCCESS".equals(jsonObject.getString("status")) ||
                "PROCESSING".equals(jsonObject.getString("status")))) {
            memberRefund.setRealRefundTime(DateUtil.date());
            baseMemberRefundService.updateById(memberRefund);
            return new WxRefundResponseDTO(true, "成功");
        } else {
            return new WxRefundResponseDTO(false, jsonObject.getString("message"));
        }
    }
}
